using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using The_Last_Knights.Helper;
using The_Last_Knights.Helper.Xml;


namespace The_Last_Knights.Event
{
    /// <summary>
    /// Day la mau SingleTon --> de tat ca cac class deu co the truy xuat
    /// </summary>
    public class EventRoutingManager : GameComponent
    {
        public enum EVENTSTATE
        {
            FINISHED = 1,
            RUNNING = 0,
            NOTRUN = -1,
            UNKNOWN = -2
        }

        private static EventRoutingManager _data;

        List<EventRouting> _readyToSends;

        Dictionary<string, EVENTSTATE> _eventState;

        Dictionary<string, List<EventEntity>> _listeners;
        
        List<EventEntity> _running;

        EventQueue _queue;

        public static EventRoutingManager GetInstance(Game game)
        {
            if (_data == null)
                _data = new EventRoutingManager(game);
            return _data;
        }

        private EventRoutingManager(Game game)
            : base(game)
        {         
            _readyToSends = new List<EventRouting>();
            _eventState = new Dictionary<string, EVENTSTATE>();
            _running = new List<EventEntity>();
            _queue = new EventQueue();
            _listeners = new Dictionary<string, List<EventEntity>>();
            EventFinished += AddEventToQueue;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            List<XElement> elements = XmlReader.Read(GameAssertPath.XML_GAMEENTITY_ELEMENT, "Event", null);
            foreach (var xElement in elements)
            {
                string id = xElement.Attribute(XName.Get(@"ID")).Value;
                _eventState.Add(id, EVENTSTATE.NOTRUN);
                _listeners.Add(id, new List<EventEntity>());
            }
            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            foreach (var eventEntity in _queue.Remove)
            {
                _running.Remove(eventEntity);                
            }
            _queue.Remove.Clear();

            foreach (var eventRouting in _readyToSends)
            {
                foreach (var listener in eventRouting.Listeners)
                {
                    listener.OnEvent(eventRouting.Sender, this, eventRouting.EventId, eventRouting.WParam, eventRouting.LParam);                    
                }
            }
            _readyToSends.Clear();

            foreach (var eventEntity in _running)
            {
                eventEntity.Update(gameTime);
            }
            base.Update(gameTime);
        }

        public event RpgGame.GlobalEvent EventFinished;

        public bool OnEventFinished(object sender, string eventid, object wparam, object lparam)
        {
            RpgGame.GlobalEvent handler = EventFinished;
            if (handler != null) 
                return handler(this, sender, eventid, wparam, lparam);
            return false;
        }

        public enum EVENT_BEHAVIOR
        {
            EVENT_FINISH = 0,
            EVENT_READYTOSTART = 1
        }

        /// <summary>
        /// Ham nay se luu giu cac event da ket thuc va cac event chuan bi duoc them vao de cap nhat vao dot Update tiep theo
        /// </summary>
        /// <param name="src"></param>
        /// <param name="sender"></param>
        /// <param name="eventID"></param>
        /// <param name="wParam">Trang thai cua event</param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private bool AddEventToQueue(object src, object sender, string eventID, object wParam, object lParam)
        {
            EVENT_BEHAVIOR state = (EVENT_BEHAVIOR)wParam;
            switch (state)
            {
                case EVENT_BEHAVIOR.EVENT_FINISH:
                    _queue.Remove.Add(sender as EventEntity);
                    break;
                case EVENT_BEHAVIOR.EVENT_READYTOSTART:
                    _queue.Add.Add(sender as EventEntity);
                    break;
            }
            return true;
        }

        /// <summary>
        /// 1 event co the dang ki de duoc chay thong qua ham nay
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool RegisterToRun(EventEntity obj)
        {
            if (_running.Contains(obj))
                return false;
            _running.Add(obj);
            return true;
        }
    
        /// <summary>
        /// Dang ki de lang nghe thong bao ve trang thai cua 1 event nao do
        /// </summary>
        /// <param name="observerID">Event dang ki</param>
        /// <param name="eventID">ID cua event muon lang nghe</param>
        /// <returns></returns>
        public bool RegisterListener(EventEntity observerID, string eventID)
        {
            if (_listeners.ContainsKey(eventID))
            {
                List<EventEntity> entities = _listeners[eventID];
                if (entities == null)
                    entities = new List<EventEntity>();
                entities.Add(observerID);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Thong bao de go bo khoi danh sach lang nghe 1 event
        /// </summary>
        /// <param name="obj">Event thong bao</param>
        /// <param name="eventID">ID cua event muon go bo</param>
        /// <returns></returns>
        public bool UnregisterListener(EventEntity obj, string eventID)
        {
            if (_listeners.ContainsKey(eventID))
            {
                List<EventEntity> entities = _listeners[eventID];
                if (entities == null)
                    entities = new List<EventEntity>();
                entities.Remove(obj);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Lay ra trang thai cua 1 event trong he thong
        /// </summary>
        /// <param name="eventID">EventID</param>
        /// <returns></returns>
        public EVENTSTATE GetEventState(string eventID)
        {
            if (_eventState.ContainsKey(eventID))
            {
                return _eventState[eventID];
            }
            return EVENTSTATE.UNKNOWN;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="EventID"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public bool ChangeEventState(EventEntity sender, string EventID, EVENTSTATE state)
        {
            if (_eventState.ContainsKey(EventID))
            {
                _eventState[EventID] = state;
                EventRouting eventRouting = new EventRouting(sender, EventID, null, null, _listeners[EventID]);
                _readyToSends.Add(eventRouting);
            }
            return false;
        }
    }
}
